#!/bin/bash
set -euo pipefail

# --- 初始化脚本目录变量 ---
# 获取脚本真实路径（兼容软链接和直接执行）
SCRIPT_DIR=$(dirname "$(readlink -f "${BASH_SOURCE[0]}" || realpath "${BASH_SOURCE[0]}")")

# 修复：安全获取用户家目录
if [ -n "${SUDO_USER:-}" ]; then
    USER_HOME=$(getent passwd "$SUDO_USER" | cut -d: -f6)
else
    USER_HOME="$HOME"
fi
ESP_DIR="$USER_HOME/esp"

# 配置常量
ESP_DIR="$HOME/esp"
IDF_VERSION="v5.2.5"
TARGET_PROJECT="uart_SRE"
QUIET_MODE=true  # 确保这里设置为 true
TAR_NAME=uart_SRE.tar

# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'

# 错误输出
error() {
    local timestamp=$(date +"%Y-%m-%d %H:%M:%S")
    echo -e "${RED}[${timestamp} Error] $*${NC}" >&2
    read -p "是否继续执行脚本？(Y/N): " user_input
    if [[ "$user_input" =~ ^[Nn]$ ]]; then
        exit 1
    fi
}

# 警告输出
warn() {
    local timestamp=$(date +"%Y-%m-%d %H:%M:%S")
    echo -e "${YELLOW}[${timestamp} Warn] $*${NC}" >&2
}

# 信息输出
info() {
    local timestamp=$(date +"%Y-%m-%d %H:%M:%S")
    echo -e "${GREEN}[${timestamp} Info] $*${NC}"
}

# 检查是否以 root 运行
check_root() {
    if [[ $EUID -eq 0 ]]; then
        warn "不建议直接以 root 运行，将尝试以当前用户权限执行"
        export HOME="$USER_HOME"
    fi
}

# 为检测到的 ESP 设备烧录固件并打开串口监视器
run_cmd() {
    local cmd=("$@")
    # 仅在安装包时提权
    if [[ $EUID -ne 0 ]] && [[ "$1" == "apt-get" ]]; then
        cmd=("sudo" "${cmd[@]}")
    fi
    if [ "$QUIET_MODE" = false ]; then
        info "执行: ${cmd[*]}"
        if ! "${cmd[@]}"; then
            error "命令执行失败: ${cmd[*]}"
        fi
    else
        if ! "${cmd[@]}" >/dev/null 2>&1; then
            error "命令执行失败: ${cmd[*]}\n完整错误日志请关闭安静模式查看"
        fi
    fi
}

# 检查依赖是否安装成功
check_dependencies() {
    local deps=("$@")
    local missing_deps=()

    for dep in "${deps[@]}"; do
        if ! dpkg-query -W -f='${Status}' "$dep" 2>/dev/null | grep -q "ok installed"; then
            missing_deps+=("$dep")
        fi
    done

    if [ ${#missing_deps[@]} -gt 0 ]; then
        warn "检测到缺失的依赖包: ${missing_deps[*]}"
        info "重新安装缺失的依赖包..."
        run_cmd apt-get install -y --fix-missing "${missing_deps[@]}"
    else
        info "所有依赖包已正确安装"
    fi
}

# 安装 ESP-IDF 所需的系统依赖包
setup_environment() {
    info "安装系统依赖..."
    local deps=(
        git wget flex bison gperf python3 python3-pip
        python3-venv cmake ninja-build ccache libffi-dev
        libssl-dev dfu-util libusb-1.0-0 minicom plocate
    ) # 额外plocate包，用于定位
    run_cmd apt-get update   # 更新软件源
    run_cmd apt-get install -y --fix-missing "${deps[@]}" # 安装依赖

    # 检查依赖是否安装成功
    check_dependencies "${deps[@]}"
}

# 克隆 ESP-IDF 仓库
load_clone_esp32() {
    local target_dir="$1"

    if [[ ! -d "$target_dir" ]]; then
        info "正在克隆 ESP-IDF [$IDF_VERSION] 到: $target_dir"
        run_cmd git clone -b "$IDF_VERSION" --recursive \
            https://github.com/espressif/esp-idf.git "$target_dir"
    else
        # 检查现有仓库版本
        local current_version
        current_version=$(git -C "$target_dir" describe --tags 2>/dev/null)
        if [[ "$current_version" != "$IDF_VERSION" ]]; then
            warn "现有版本 $current_version 与目标版本 $IDF_VERSION 不符"
            run_cmd git -C "$target_dir" fetch --all
            run_cmd git -C "$target_dir" checkout "$IDF_VERSION"
            run_cmd git -C "$target_dir" submodule update --init --recursive
        fi
    fi
}

# 安装工具链
load_install_esp32() {
    local esp_idf_dir="$1"


    info "正在安装 ESP32 工具链..."
    (
        cd "$esp_idf_dir" || error "无法进入目录: $esp_idf_dir"
        run_cmd ./install.sh esp32
    ) || error "工具链安装失败"

}

# 加载环境变量
load_idf_env() {
    local esp_idf_dir="$1"
    local export_script="$esp_idf_dir/export.sh"

    [[ -f "$export_script" ]] || error "未找到环境脚本: $export_script"

    info "加载 ESP-IDF 环境变量..."
    source "$export_script" > /dev/null 2>&1 || return 1

    # 二次验证
    command -v idf.py >/dev/null 2>&1 || {
        warn "idf.py 命令未找到"
        return 1
    }
    return 0
}

# 配置 ESP-IDF 环境
setup_esp_idf() {
    local esp_idf_dir="$ESP_DIR/esp-idf"  #$ESP_DIR="$HOME/esp"

    # 创建 ESP 目录结构
    mkdir -p "$ESP_DIR" || error "无法创建目录: $ESP_DIR"

    # 步骤 1: 克隆仓库（带版本控制）
    load_clone_esp32 "$esp_idf_dir"

    # 步骤 2: 安装工具链
    load_install_esp32 "$esp_idf_dir"

    # 步骤 3: 加载环境变量
    if ! load_idf_env "$esp_idf_dir"; then
        error "ESP-IDF 环境加载失败，请检查路径: $esp_idf_dir"
    fi

    # 验证环境
    if [[ -z "$IDF_PATH" ]]; then
        error "IDF_PATH 环境变量未正确设置"
    fi
    info "IDF_PATH 已配置: $IDF_PATH"
}

# 解压并配置目标项目
setup_project() {
    local project_dir="$ESP_DIR/$TARGET_PROJECT"
    local tar_path="$SCRIPT_DIR/$TAR_NAME"  # 始终使用脚本目录

    # 检查项目是否需要初始化
    if [[ ! -f "$project_dir/CMakeLists.txt" ]]; then
        # 验证压缩包是否存在
        if [[ ! -f "$tar_path" ]]; then
            error "关键文件缺失！\n请检查 $TAR_NAME 必须与脚本位于同一目录: $SCRIPT_DIR"
        fi

        # 解压操作
        info "从脚本目录解压: $(basename "$tar_path")"
        if ! tar -xf "$tar_path" -C "$ESP_DIR" 2>/dev/null; then
            error "解压失败！"
        fi

        # 验证解压结果
        local cmake_path="$project_dir/CMakeLists.txt"
        if [[ ! -f "$cmake_path" ]]; then
            error "解压后未找到 $cmake_path\n请检查压缩包结构是否包含顶层目录"
        fi
    else
        info "项目已存在: $project_dir"
    fi
}

# 构建项目:清理、设置目标芯片并编译项目
build_project() {
    local project_dir="$ESP_DIR/$TARGET_PROJECT"

    info "清理项目..."
    (cd "$project_dir" && run_cmd idf.py fullclean)

    info "设置目标芯片..."
    (cd "$project_dir" && run_cmd idf.py set-target esp32)

    info "编译项目..."
    if ! (cd "$project_dir" && idf.py build); then
        warn "编译失败，尝试修复..."
        (cd "$project_dir" && run_cmd idf.py fullclean)
        (cd "$project_dir" && run_cmd idf.py build)
    fi
}

# 检测 ESP 设备
detect_esp_devices() {
    local devices=()
    for dev in /dev/ttyUSB*; do
        [[ -e "$dev" ]] && devices+=("$dev")
    done
    echo "${devices[@]}"
}

# 烧录设备
flash_device() {
    while true; do
        local devices=($(detect_esp_devices))
        if [[ ${#devices[@]} -eq 0 ]]; then
            read -p "未检测到设备，请插入设备后按回车重试或输入 'exit' 退出: " user_input
            if [[ "$user_input" == "exit" ]]; then
                break
            fi
            continue
        fi

        # 列出设备供用户选择
        PS3="请选择要烧录的设备（输入编号）: "
        select device in "${devices[@]}" "全部烧录" "重新检测" "退出"; do
            case $device in
                "全部烧录")
                    for dev in "${devices[@]}"; do
                        # 烧录逻辑
                        info "检测到设备: $dev"
                        run_cmd sudo chmod 777 "$dev"

                        # 烧录
                        (
                            cd "$ESP_DIR/$TARGET_PROJECT" || error "目录不存在"
                            if idf.py -p "$dev" flash; then
                                info "烧录成功 ✅"
                            else
                                error "烧录失败 ❌ 错误码: $?"
                                return 1
                            fi
                        )

                        # 串口监视
                        #info "打开串口监视器（30秒后自动关闭）..."
                        #timeout 30 minicom -D "$dev"
                        #stty sane
                    done
                    read -p "所有设备烧录完成。按回车继续或输入 'exit' 退出: " user_input
                    if [[ "$user_input" == "exit" ]]; then
                        break 2
                    else
                        break
                    fi
                    ;;
                "重新检测")
                    devices=($(detect_esp_devices))
                    if [[ ${#devices[@]} -eq 0 ]]; then
                        echo "未检测到设备。"
                    else
                        echo "检测到的设备:"
                        printf '%s\n' "${devices[@]}"
                    fi
                    break
                    ;;
                "退出")
                    break 2
                    ;;
                *)
                    if [[ -n "$device" ]]; then
                        # 烧录逻辑
                        info "检测到设备: $device"
                        run_cmd sudo chmod 777 "$device"

                        # 烧录
                        (
                            cd "$ESP_DIR/$TARGET_PROJECT" || error "目录不存在"
                            if idf.py -p "$device" flash; then
                                info "烧录成功 ✅"
                            else
                                error "烧录失败 ❌ 错误码: $?"
                                return 1
                            fi
                        )

                        # 串口监视
                        #info "打开串口监视器（30秒后自动关闭）..."
                        #timeout 30 minicom -D "$device"
                        #stty sane

                        read -p "烧录完成。按回车继续或输入 'exit' 退出: " user_input
                        if [[ "$user_input" == "exit" ]]; then
                            break 2
                        else
                            break
                        fi
                    else
                        echo "无效选择，请重试。"
                    fi
                    ;;
            esac
        done
    done
}

# 主流程
main() {
    check_root
    mkdir -p "$ESP_DIR"

    setup_environment
    setup_esp_idf
    setup_project
    build_project
    flash_device
}

# 执行主函数
main
